package ar.edu.unlu.sistemas.p2p.business.task;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

import ar.edu.unlu.sistemas.p2p.business.module.AppModule;
import ar.edu.unlu.sistemas.p2p.business.msg.MessageFactory;
import ar.edu.unlu.sistemas.p2p.business.util.Logger;
import ar.edu.unlu.sistemas.p2p.business.util.Params;
import ar.edu.unlu.sistemas.p2p.business.vo.PeerResultsVO;

public class Task extends Thread {
	
	private String msgId;
	private String sourcePeerId;
	private HashMap<String,PeerResultsVO> results = new HashMap<String,PeerResultsVO>();
	private boolean finished = false;
	private int msgType;
	
	public int getMsgType() {
		return msgType;
	}

	public void setMsgType(int msgType) {
		this.msgType = msgType;
	}

	public String getSourcePeerId() {
		return sourcePeerId;
	}

	public void setSourcePeerId(String sourcePeerId) {
		this.sourcePeerId = sourcePeerId;
	}

	public boolean isFinished() {
		return finished;
	}

	public void setFinished(boolean finished) {
		this.finished = finished;
	}

	public String getMsgId() {
		return msgId;
	}
	
	public void setMsgId(String msgId) {
		this.msgId = msgId;
	}
	
	private HashMap<String,PeerResultsVO> getResults() {
		return results;
	}
	
	private void setResults(HashMap<String,PeerResultsVO> results) {
		this.results = results;
	}
	
	/**
	 * Pregunta si se obtuvo respuesta de todos los peers a los que se les propago el mensaje
	 * 
	 * @return
	 */
	private boolean isAllCompleted() {
		Iterator<String> keys = this.getResults().keySet().iterator();
		boolean flag = true;
		while (keys.hasNext() && flag) {
			flag = flag && this.getResults().get(keys.next()).hasResults();
		}
		return flag;
	}
	
	/**
	 * Pregunta si no se obtuvo exactamente ninguna respuesta de todos los peers a los que 
	 * se les propago el mensaje 
	 * 
	 * @return
	 */
	private boolean isAllEmpty() {
		Iterator<String> keys = this.getResults().keySet().iterator();
		boolean flag = true;
		while (keys.hasNext() && flag) {
			flag = flag && !this.getResults().get(keys.next()).hasResults();
		}
		return flag;
	}
	
	public Task(String msgId) {
		this.msgId = msgId;
	}
	
	public Task(String msgId, String peerId, int msgType) {
		this.msgId = msgId;
		this.sourcePeerId = peerId;
		this.msgType = msgType;
	}
	
	/**
	 * Agrega un peer a la lista que representa los peers de los que se espera un resultado 
	 * 
	 * @param peerId
	 */
	public synchronized void addPeer(String peerId) {
		if (!this.getResults().containsKey(peerId)) {
			Logger.logInfo(this.getClass().getName(), "Agrego peer "+peerId+" al task "+this.getMsgId());
			this.getResults().put(peerId, new PeerResultsVO(peerId));
		} else {
			Logger.logInfo(this.getClass().getName(), "Task "+this.getMsgId()+ " ya contiene al peer "+peerId);
		}
	}
	
	/**
	 * Se obtiene la respuesta por parte de un peer y se agrega a la lista
	 * 
	 * @param peerId
	 * @param results
	 */
	public synchronized void addPeerResults(String peerId, Collection<Result> results) {
		if (this.getResults().containsKey(peerId)) {
			this.getResults().get(peerId).addResults(results);
		} else {
			Logger.logInfo(this.getClass().getName(), "No existe entrada para el peer "+peerId);
		}
	}
	
	/**
	 * Se obtiene la respuesta por parte de un peer y se agrega a la lista
	 * 
	 * @param peerId
	 * @param results
	 */
	public synchronized void addPeerResult(String peerId, Result result) {
		if (!this.getResults().containsKey(peerId)) {
			this.getResults().get(peerId).addResult(result);
		}
	}
	
	/**
	 * Se combinan todos los resultados obtenidos por parte de los peers y se retornan
	 * 
	 * @return
	 */
	public synchronized Collection<Result> getAllResults() {
		Collection<Result> out = new ArrayList<Result>();
		Iterator<String> keys = this.getResults().keySet().iterator();
		while (keys.hasNext()) {
			PeerResultsVO vo = this.getResults().get(keys.next());
			if (vo.hasResults()) {
				out.addAll(vo.getResults());
			}
		}
		return out;
	}
	
	public void run() {
		int i =  0;
		while (!this.isFinished()) {
			try {
				Thread.sleep(60);
			} catch (InterruptedException ie) {
				
			}
			if (this.isAllCompleted() || (i >= Params.WAITING_TASK_ITERATIONS)) {
				this.setFinished(true);
			}
			i++;
		}
		Logger.logInfo(this.getClass().getName(), "Finalizo task");
		if (i >= Params.WAITING_TASK_ITERATIONS) {
			Logger.logInfo(this.getClass().getName(), "Expiro el tiempo de espera");
		}
		if (this.getSourcePeerId() != null) {
			switch (this.getMsgType()) {
				case MessageFactory.CODE_PING:
					AppModule.getInstance().sendPong(this.getMsgId());
					break;
				case MessageFactory.CODE_QUERY:
					AppModule.getInstance().sendQueryResponse(this.getMsgId());
					break;
				default:
					break;
			}
			
		}
	}

}
